package utils

import archer.kunlun.common.models.logging.{ LoggingTypes, 程序异常 }
import cats.{ Monad, StackSafeMonad }
import cats.implicits._
import models.TableData
import play.api.libs.circe.Circe
import play.api.mvc.{ Result, Results }
import io.circe._
import io.circe.syntax._
import io.monadless.cats.MonadlessMonad
import net.scalax.errmiao._
import org.slf4j.Logger
import play.api.http.{ Status, Writeable }
import slick.SlickException
import slick.dbio.DBIO
import slick.lifted.Query

import scala.concurrent.{ ExecutionContext, Future }
import scala.language.higherKinds
import scala.language.implicitConversions

trait CusResult extends Circe with ArcherHelper

trait ArcherHelper extends ErrMiaoHelper {

  implicit def queryExtra[E, U, C[_]](
    query: Query[E, U, C]): RichQuery[E, U, C] = {
    val query1 = query
    new RichQuery[E, U, C] {
      override val query = query1
    }
  }

  def dbioLess(implicit ec: ExecutionContext): MonadlessMonad[DBIO] =
    MonadlessMonad[DBIO]()

  val initParam: SlickParam = SlickParam.empty

  object R {
    def toJson[T](value: V.ResultT[T])(implicit
      encoder: Encoder[T],
      ec: ExecutionContext): Future[Json] = {
      value.fold({ errorResult =>
        TableData.msg(errorResult.msg, errorResult.code).asJson
      }, { data =>
        data.asJson
      })
    }

    def toJsonResult[T](value: V.ResultT[TableData[T]])(
      implicit
      encoder: Encoder[T],
      writeable: Writeable[Json],
      ec: ExecutionContext): Future[Result] = {
      futureLess.lift {
        val tableData = futureLess.unlift(toJson(value))
        val resultF = value.fold({ errorResult =>
          new Results.Status(errorResult.code).apply(tableData)
        }, { data =>
          new Results.Status(data.code).apply(tableData)
        })
        futureLess.unlift(resultF)
      }
    }
  }

  override val V: CommonValue = RicherValue

  implicit def catsStdInstancesForDBIO(
    implicit
    ec: ExecutionContext): Monad[DBIO] =
    new Monad[DBIO] with StackSafeMonad[DBIO] {
      override def pure[A](x: A): DBIO[A] = DBIO.successful(x)

      override def flatMap[A, B](fa: DBIO[A])(f: A => DBIO[B]): DBIO[B] =
        fa.flatMap(f)

      override def map[A, B](fa: DBIO[A])(f: A => B): DBIO[B] = fa.map(f)
    }

}

object ArcherHelper extends ArcherHelper
/*trait LogArcherHelper extends ArcherHelper {

  val loggingService: LoggingService

  override lazy val commonCatch: FutureExceptionCatch = {
    new DBFutureExceptionCatch { }
  }

  trait DBFutureExceptionCatch extends FutureExceptionCatch {
    override def loggerOperation(logMsg: EMsg, exception: Throwable)(implicit ec: ExecutionContext): Future[Boolean] = {
      logger.error(logMsg.autalLogMsg, exception)
      loggingService.pushInfoJsonLog(LoggingTypes.程序异常, 程序异常(
        额外信息 = logMsg.autalLogMsg,
        提示信息 = logMsg.user,
        异常信息 = exception.getMessage)).map(_ => true)
    }
  }

}*/
trait RicherValue extends CommonValue {
  override def commonCatchGen[T](logger: Logger): RicherFutureExceptionCatch = {
    val logger1 = logger
    new RicherFutureExceptionCatch {
      override def loggerOperation(logMsg: EMsg, exception: Throwable)(
        implicit
        ec: ExecutionContext): Future[Boolean] = {
        Future {
          logger1.error(logMsg.autalLogMsg, exception)
          true
        }
      }
    }
  }

  trait RicherFutureExceptionCatch extends FutureExceptionCatch {
    self =>

    override def apply[T](future: Future[T])(
      implicit
      ec: ExecutionContext): CommonValue.ResultT[T] = {
      apply(future, EMsg(user = "未知错误", log = "不可识别的异常"))
    }

    override def parGen(logMsg: EMsg)(implicit ec: ExecutionContext): PartialFunction[Throwable, ErrorResult] = {
      val currPar: PartialFunction[Throwable, ErrorResult] = {
        case slicke: SlickException =>
          val defaultMas = "数据库错误"
          loggerOperation(logMsg.copy(log = Option(defaultMas)), slicke)
          ErrorResult(logMsg.user, code = Status.INTERNAL_SERVER_ERROR)
      }

      {
        case e if currPar.isDefinedAt(e) =>
          currPar(e)
        case e if super.parGen(logMsg).isDefinedAt(e) =>
          super.parGen(logMsg).apply(e)
      }
    }
  }
}

object RicherValue extends RicherValue
